Repository navigation
Script Loader: Add an API for filtering inline style attributes - #13250
Sukhendu2002 wants to merge 75 commits into
Conversation
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Replace substring and exact-string assertions with assertEqualHTML() for fully deterministic output, so attribute order, quoting style and self-closing syntax no longer cause spurious failures. Use WP_HTML_Tag_Processor where full-markup comparison is a poor fit: the core concatenation test (whose load-styles.php URL is noisy) and the closing-tag escaping test, which now checks that the CSS cannot break out of the STYLE element rather than pinning the exact escape sequence. Also verify the attributes passed to the wp_inline_style_attributes filter during normal style output. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Widen the documented attribute value type from string|true to string|bool for wp_get_inline_style_tag(), wp_print_inline_style_tag() and the wp_inline_style_attributes filter. This matches wp_get_script_tag() and wp_get_inline_script_tag(), and reflects that a false value omits the attribute, as the filter docblock already describes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When wp_hoist_late_printed_styles() splits CSS added via wp_add_inline_style( 'wp-block-library', ... ) into a separate wp-block-library-inline-css-extra STYLE tag, build that tag with wp_get_inline_style_tag() so it receives the attributes from the wp_inline_style_attributes filter. Previously the original tag had a filtered nonce but the split-off tag did not, so a nonce-based Content Security Policy would block the CSS after hoisting. Add a hoisting test data set which filters in a nonce, and loosen the global styles placeholder pattern so that it still matches the placeholder STYLE tag when it has additional attributes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In WP_Styles::do_item(), the inline style for a handle is already obtained up front via print_inline_style( $handle, false ). When not concatenating, calling print_inline_style( $handle ) again re-read the after data and rebuilt the sourceURL comment. Instead, append the inline style tag to the LINK tag once, and then print or buffer the result, which also removes the duplicated wp_get_inline_style_tag() call between the concat and non-concat branches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Print the remaining hand-built front-end STYLE tags via wp_print_inline_style_tag() so that they also receive attributes from the wp_inline_style_attributes filter, such as a nonce for a Content Security Policy: * Custom background (including the empty Customizer preview tag). * Additional CSS from the Customizer. * Custom logo header text hiding. * Block support styles. * Font faces. * Recent Comments widget. Add a data-provider test covering each STYLE tag printed outside of WP_Styles, and extend the hoisting test data set with a filtered nonce to assert that every STYLE tag in the page has it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The core inline style data sets add and remove theme support, which is not restored automatically between tests. The leaked custom-background support caused WP_Test_REST_Themes_Controller to fail with an undefined $custom_background global when run after these tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Print the error page styles with wp_print_inline_style_tag() so that they receive attributes from the wp_inline_style_attributes filter. Since wp_die() may be called before script-loader.php and the HTML API are loaded, fall back to a literal STYLE tag in that case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Print the transparent background style for the interim login modal with wp_print_inline_style_tag() so that it receives filtered attributes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Print the styles for wp-signup.php and wp-activate.php with wp_print_inline_style_tag() so that they receive filtered attributes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Print the banner background styles in the plugin information modal with wp_print_inline_style_tag() so that they receive filtered attributes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Print the site icon URL custom property with wp_print_inline_style_tag() so that it receives filtered attributes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Print the Customizer preview loading styles, the highlighted widget styles and the site icon control template styles with wp_print_inline_style_tag() so that they receive filtered attributes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Print the site icon URL custom property in the tmpl-site-icon-preview-crop template with wp_print_inline_style_tag() so that it receives filtered attributes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Build the gallery shortcode's default STYLE tag with wp_get_inline_style_tag() so that it receives filtered attributes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Print the reset styles in the Legacy Widget block preview and the REST API widget preview iframe with wp_print_inline_style_tag() so that they receive filtered attributes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Trim the complete set of whitespace characters defined by CSS (space, tab, form feed, carriage return and line feed) from the start and end of the CSS passed to wp_get_inline_style_tag(), rather than an arbitrary subset. This is the same set, in the same order, that the HTML API uses. Co-Authored-By: Jon Surrell <jonsurrell@git.wordpress.org> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add a test that leading and trailing CSS whitespace always becomes a single newline wrapping the contents of the STYLE element, with the handling of null data from a misbehaving filter folded into it. Remove the test that a closing STYLE tag in the CSS is escaped, since that is the HTML API's responsibility and is already covered by the tests for WP_HTML_Tag_Processor::set_modifiable_text(). Co-Authored-By: Jon Surrell <jonsurrell@git.wordpress.org> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The test for calling WP_Styles::print_inline_style() directly exercised the same path as the test for normal style output, since that is how inline styles are printed when concatenation is off. Co-Authored-By: Jon Surrell <jonsurrell@git.wordpress.org> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sirreal
left a comment
There was a problem hiding this comment.
The attribute filter here (and already existing on script tag function) should be passed the script tag data from the processor instead of the original data before setting.
|
@Sukhendu2002 Do you want to action on Jon's feedback? |
sirreal
left a comment
There was a problem hiding this comment.
Thanks, this looks good to me.
Replace the closures that wrote the filter arguments to a by-reference variable with a `MockAction`, which records the arguments and call count. Where a closure is still needed to add the `nonce` attribute, a separate `MockAction` at a later priority captures what the filter is passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…style_tag()`. For a STYLE tag, `WP_HTML_Tag_Processor::set_modifiable_text()` escapes any `</style` in the text rather than rejecting it, so it always returns true. The branch returning an empty string could never run, so remove it and document the escaping instead of a failure case that does not happen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tributes` filter. A filter callback that forgets to return its value makes `$attributes` null, which caused a warning when iterating it and dropped every attribute, including the `id`. Fall back to the unfiltered attributes in that case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h `wp_inline_style_attributes`. When `wp_hoist_late_printed_styles()` removed the placeholder from the `wp-block-library` inline style, it replaced only the text of the STYLE tag. The `wp_inline_style_attributes` filter had already run on the original text, so any attribute derived from the CSS, such as a hash for a Content Security Policy, no longer matched what was printed. Rebuild the tag with `wp_get_inline_style_tag()` so that the filter runs again on the new CSS. The STYLE tag was also located by its `id`, which the filter can change or remove. In that case the placeholder was left in the page and the block styles were not inserted after the block library styles. Locate the tag by the placeholder in its text instead. Tests are added for both cases. The regular expression the test uses to find the global styles placeholder no longer assumes `id` is the first attribute. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…style_attributes` filter." This reverts commit 8841042. Guarding against a non-array return is only a partial validation of the filtered value, which would also need its keys and values checked to satisfy `WP_HTML_Tag_Processor::set_attribute()`. The equivalent `wp_inline_script_attributes` filter has no such guard and no errors have been reported from it, so the guard is unnecessary here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…S rather than their IDs. The global styles and block styles placeholder STYLE tags were located by their `id` attributes, which the `wp_inline_style_attributes` filter can change. If it did, the placeholder CSS was left in the page, and the global styles and block styles were printed at the end of HEAD instead of at their normal positions. Locate them by their placeholder CSS instead, as is now done for the block library inline style. The test now renames all three IDs, and it checks for the placeholders by their CSS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed block library inline style. The block library inline style was rebuilt with only its `id`, so any other attributes already on the tag in the output buffer were lost unless the `wp_inline_style_attributes` filter happened to add them again, for example ones added by an earlier `wp_template_enhancement_output_buffer` filter. Pass the existing attributes when rebuilding the tag, so that they are kept while the filter may still override them. This also keeps an `id` that the filter had changed. The attribute names are narrowed from `non-falsy-string` to `non-empty-string` in the PHPStan types of `wp_get_inline_style_tag()` and `wp_print_inline_style_tag()`, since `0` is a valid attribute name and the names read from the tag are typed this way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…or an inline style. Since `wp_hoist_late_printed_styles()` rebuilds the `wp-block-library` inline style after removing a placeholder from its CSS, the filter first runs on CSS that is never printed. Document this so that a filter which collects values per call, such as hashes for a Content Security Policy, can account for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ders are located. Locating the placeholder STYLE tags by their CSS means reading the text of each STYLE tag in HEAD, which copies it. Once all of the placeholders have been found, skip reading the text of the remaining STYLE tags, such as the Customizer's Additional CSS, while still looking for the end of HEAD. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
I just found something problematic with this. By allowing the attributes to be filtered, it can result in the wordpress-develop/src/js/_enqueues/wp/customize/preview.js Lines 654 to 670 in 8ce8797 There are other integrations like this in PHP, for example in wordpress-develop/src/wp-includes/script-loader.php Lines 4507 to 4531 in 8ce8797 This would break if the Perhaps the |
…placeholders are located." This reverts commit 44dfa2b. The placeholder STYLE tags will again be located by their IDs, which are being made immutable in the `wp_inline_style_attributes` filter, so their CSS no longer needs to be read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…their CSS rather than their IDs." This reverts commit ea09b6e. The IDs of inline STYLE tags are being made immutable in the `wp_inline_style_attributes` filter, since other code locates the tags by them, such as the Customizer preview with `wp-custom-css`. So the placeholder STYLE tags can be located by their IDs again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hanging a supplied ID. Other code locates inline STYLE tags by their IDs, such as the Customizer preview with `wp-custom-css` and `wp_hoist_late_printed_styles()` with its placeholders, so changing or removing an ID with the filter breaks them. When an `id` is supplied to `wp_get_inline_style_tag()`, it is now always printed, and a `_doing_it_wrong()` is issued if the filter changed or removed it. The ID is set before the filtered attributes, so a lexical variation of the name such as `ID` cannot override it either. The filter may still add an ID when none is supplied. The hoisting test case which renamed the block library ID is removed, since the rename is now ignored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…when hoisting styles. The block library inline style was located by its placeholder CSS in 8db7a9c because the `wp_inline_style_attributes` filter could change its ID. Now that the filter cannot change a supplied ID, go back to locating it by ID, consistent with the other placeholder STYLE tags, and without reading the CSS of each STYLE tag in HEAD. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
This PR now has merge conflicts after r64585 which will need to be resolved. |
What this PR does
Adds a structured API for generating and filtering inline
<style>tags, mirroring the existing inline script tag API:wp_get_inline_style_tag( $data, array $attributes = array() ).wp_print_inline_style_tag().wp_inline_style_attributesfilter, which receives the attributes and the CSS.WP_HTML_Tag_Processorfor safe attribute and CSS handling, including escaping of a closing</style>in the CSS.$attributesasarray<string, string|bool>, matchingwp_get_inline_script_tag().This allows plugins to add attributes such as CSP nonces without modifying complete HTML strings. The script portion of the original ticket is already covered by
wp_inline_script_attributes, so this PR focuses on the remaining inline style functionality.Usage in
WP_Stylesandscript-loader.phpThe
WP_Stylesoutput for normal, no-source, and concatenated inline styles, plus the hoisted extra block library styles and block support styles inscript-loader.php, now go through the new API instead of building the tag withWP_HTML_Tag_Processordirectly. This is the same way r61485 updatedWP_Scriptsalong with the script tag functions.Other STYLE tags
At the reviewer's request, the conversion of the other STYLE tags to the new API has been split into separate PRs, which depend on this one:
WP_Styles(wp-admin and wp-includes).Testing
New tests in
tests/phpunit/tests/dependencies/wpInlineStyleTag.phpandtests/phpunit/tests/dependencies/styles.phpuseassertEqualHTML()andWP_HTML_Tag_Processorto verify the output.Trac ticket: https://core.trac.wordpress.org/ticket/51325
Use of AI Tools
AI assistance: Yes
Tool(s): OpenAI Codex
Model(s): GPT-5.6
Used for: Initial code skeleton, test suggestions and code review; final implementation and tests were reviewed and edited by me.
Additional commits were made with Claude Code (Claude Opus 5.5) at the direction of @westonruter, to make the tests more robust with
assertEqualHTML()andWP_HTML_Tag_Processor, to route the remaining core<style>tags through the new API (since split into #14216 and #14217), and to address code review findings. The changes were reviewed by @westonruter.This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.
🤖 Generated with Claude Code