ewww-image-optimizer
ewww-image-optimizer copied to clipboard
Add filters to inline scripts in BODY tag for compatibility with page optimization plugins
The issue The attributes of the inline script in the BODY tag for detecting WebP support are fixed, and cannot be changed to accommodate page optimization plugins.
Enqueued scripts can be modified via the script_loader_tag filter or wp_script_add_data(), but literal insertion can only processed by buffering the entire page and post-processing it, which is bad for performance.
Proposed solution Apply filters to literal script HTML strings before adding them to the page.
Hi @galbaras did you have a particular use case in mind? Normally I'd just be like "you bet, let's filter all the things!" But in this case, escaping the filtered attributes to make it safe for output is going to require a reworking of the code involved, so it's not quite as simple as one would expect.
Can you at least provide a filter for adding script attributes? This should be easier, no?
The use case is targeting page optimization plugins, like Autoptimize, WP Fastest Cache or LiteSpeed Cache, each with its own unique ways of excluding scripts from being combined, minified and/or deferred.
Unfortunately, it doesn't really matter whether the filter is for the existing attrs or just for adding new ones, we still have to escape "user-supplied" data, and that will require a reworking of that code. Just trying to set clear expectations about this not being a quick fix/implementation.
Any page optimization plugins can easily exclude our inline scripts with the string "ewww_webp" (like WP Rocket did) and definitely should exclude scripts that use data-cfasync="false" (like WPFC naturally did).
As it turns out, LiteSpeed Cache doesn't accept arbitrary strings, only script names, which it compares against the value of src.
I think that using esc_attr( apply_filters( ... ) ) should do the trick for you. Would this work?
$body_tags = $this->get_elements_from_html( $buffer, 'body' );
$script_attr = esc_attr( apply_filters( 'ewwwio_js_webp_script_attr', 'data-cfasync="false"' );
$body_webp_script = '<script ' . $script_attr . '>if(ewww_webp_supported){document.body.classList.add("webp-support");}</script>';
The esc_attr() function is only for the attribute value, so it will escape/encode quotes and break the markup. That's precisely what makes it tricky.
What is LS Cache doing with inline scripts? Is it combining them, deferring them, or something else?
How about passing an array of name/value pairs and combining them into a string after sanitizing each separately?
LS Cache is doing all of the above to scripts, and I can't disable inline scripts completely in this case. The plugin authors have already agreed to accommodate my specific request, but this may not be good enough for others.
WordPress provides ways to modify enqueued scripts, but you're bypassing those mechanisms (for good reason), but this creates an inconsistent situation, where the script can't be modified in the same way(s), and that's sometimes needed.
How about passing an array of name/value pairs and combining them into a string after sanitizing each separately?
Right, that's exactly the method I was thinking of. Feel free to submit a pull request if you'd like it sooner :)
Finally committed this, will be in the next release. The filtered attributes will be applied to all three JS WebP scripts, since they should typically all be handled the same. That is, no defer and no minify, and definitely no delaying.