Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide fallback JPEG images in frontend when WebP is not supported by the browser #360

Merged
merged 27 commits into from
Jul 13, 2022
Merged
Changes from 10 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
36cd431
Add the initial version of webp-hero script.
eugene-manuilov May 24, 2022
11197de
Fix formatting issues.
eugene-manuilov May 24, 2022
8262fca
Merge remote-tracking branch 'origin/trunk' into enhancement/341-wepb…
eugene-manuilov Jun 9, 2022
e06409b
Update webp-hero hook to be a fallback.
eugene-manuilov Jun 9, 2022
6637709
Add mime types trasform checks to make sure we add the fallback if jp…
eugene-manuilov Jun 10, 2022
5cb1283
Updated the fallback script to process images that was added while we…
eugene-manuilov Jun 10, 2022
7044a6d
Updated the fallback script to use REST API to get image details.
eugene-manuilov Jun 10, 2022
45a5fde
Updated the fallback script to pull media details in batches.
eugene-manuilov Jun 10, 2022
0d91c53
Fix js issues that prevents images to work in IE9.
eugene-manuilov Jun 10, 2022
6ffde35
Address code review feedback.
eugene-manuilov Jun 13, 2022
829390a
Merge remote-tracking branch 'origin/trunk' into enhancement/341-wepb…
eugene-manuilov Jun 15, 2022
9b280a6
Address code review feedback.
eugene-manuilov Jun 15, 2022
5365f64
Removed unnecessary check.
eugene-manuilov Jun 15, 2022
d97c430
Address code review feedback.
eugene-manuilov Jun 22, 2022
4fbb95e
Merge remote-tracking branch 'origin/trunk' into enhancement/341-wepb…
eugene-manuilov Jun 22, 2022
49fca5e
Update the condition to register fallback hook only if it hasn't been…
eugene-manuilov Jun 22, 2022
e44a8a2
Merge remote-tracking branch 'origin/trunk' into enhancement/341-wepb…
eugene-manuilov Jun 24, 2022
4db66ba
Add phpunit tests.
eugene-manuilov Jun 24, 2022
74fe886
Merge remote-tracking branch 'origin/trunk' into enhancement/341-wepb…
eugene-manuilov Jul 7, 2022
f205815
Address code review feedback.
eugene-manuilov Jul 8, 2022
ea38eae
Merge remote-tracking branch 'origin/trunk' into enhancement/341-wepb…
eugene-manuilov Jul 8, 2022
a1fb857
Avoid JS error in case JPEG source does not exist for an image.
felixarntz Jul 12, 2022
a9bf111
Make printing inline script tag more defensive.
felixarntz Jul 12, 2022
6871972
Use get_echo() utility in tests.
felixarntz Jul 12, 2022
9429f1b
Fix syntax error :D
felixarntz Jul 12, 2022
696ade3
Merge remote-tracking branch 'origin/trunk' into enhancement/341-wepb…
eugene-manuilov Jul 13, 2022
7e70d49
Merge branch 'enhancement/341-wepb-hero' of github.com:WordPress/perf…
eugene-manuilov Jul 13, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
99 changes: 99 additions & 0 deletions modules/images/webp-uploads/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -573,3 +573,102 @@ function webp_uploads_update_featured_image( $html, $post_id, $attachment_id ) {
return webp_uploads_img_tag_update_mime_type( $html, 'post_thumbnail_html', $attachment_id );
}
add_filter( 'post_thumbnail_html', 'webp_uploads_update_featured_image', 10, 3 );

/**
* Adds a fallback mechanism to replace webp images with jpeg alternatives on older browsers.
*
* @since n.e.x.t
*/
function webp_uploads_wepb_fallback() {
// Get mime type transofrms for the site.
$transforms = webp_uploads_get_upload_image_mime_transforms();
if ( ! is_array( $transforms ) ) {
return;
}
eugene-manuilov marked this conversation as resolved.
Show resolved Hide resolved

// We need to add fallback only if jpeg alternatives for the webp images are enabled for the server.
$preserve_jpegs_for_jpeg_transforms = in_array( 'image/jpeg', $transforms['image/jpeg'], true ) && in_array( 'image/webp', $transforms['image/jpeg'], true );
eugene-manuilov marked this conversation as resolved.
Show resolved Hide resolved
$preserve_jpegs_for_webp_transforms = in_array( 'image/jpeg', $transforms['image/webp'], true );
eugene-manuilov marked this conversation as resolved.
Show resolved Hide resolved
if ( ! $preserve_jpegs_for_jpeg_transforms && ! $preserve_jpegs_for_webp_transforms ) {
felixarntz marked this conversation as resolved.
Show resolved Hide resolved
return;
}

?>
<script>
eugene-manuilov marked this conversation as resolved.
Show resolved Hide resolved
eugene-manuilov marked this conversation as resolved.
Show resolved Hide resolved
( function() {
window._fallbackWebpImages = function( media ) {
for ( var i = 0; i < media.length; i++ ) {
try {
var ext = media[i].media_details.sources['image/jpeg'].file.match( /\.\w+$/i );
eugene-manuilov marked this conversation as resolved.
Show resolved Hide resolved
if ( ! ext || ! ext[0] ) {
continue;
}

var images = document.querySelectorAll( 'img.wp-image-' + media[i].id );
for ( var j = 0; j < images.length; j++ ) {
images[j].src = images[j].src.replace( /\.webp$/i, ext[0] );
var srcset = images[j].getAttribute( 'srcset' );
if ( srcset ) {
images[j].setAttribute( 'srcset', srcset.replace( /\.webp(\s)/ig, ext[0] + '$1' ) );
}
}
} catch ( e ) {
eugene-manuilov marked this conversation as resolved.
Show resolved Hide resolved
}
}
};

var loadMediaDetails = function( nodes ) {
var ids = [];
for ( var i = 0; i < nodes.length; i++ ) {
if ( nodes[i].nodeName !== "IMG" || ! nodes[i].src.match( /\.webp$/i ) ) {
continue;
}

var attachment = nodes[i].className.match( /wp-image-(\d+)/i );
if ( attachment && attachment[1] && ids.indexOf( attachment[1] ) === -1 ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small suggestion would be to replace the array with an object structure in that sense you don't need to check if the item exists as you can set it regardless like:

ids[ attachment[1] ] = true;

Then you can just return the keys from it, if you only need the values:

return Object.keys( ids );

ids.push( attachment[1] );
}
}

for ( var page = 0, pages = Math.ceil( ids.length / 100 ); page < pages; page++ ) {
var pageIds = [];
for ( var i = 0; i < 100 && i + page * 100 < ids.length; i++ ) {
eugene-manuilov marked this conversation as resolved.
Show resolved Hide resolved
pageIds.push( ids[ i + page * 100 ] );
}

var jsonp = document.createElement( 'script' );
jsonp.src = '<?php echo esc_js( get_rest_url() ); ?>wp/v2/media/?_fields=id,media_details&_jsonp=_fallbackWebpImages&per_page=100&include=' + pageIds.join( ',' );
document.body.appendChild( jsonp );
}
};
felixarntz marked this conversation as resolved.
Show resolved Hide resolved

var img = document.createElement( 'img' );

// Verify two webp images.
img.src = "data:image/webp;base64,UklGRjIAAABXRUJQVlA4ICYAAACyAgCdASoCAAEALmk0mk0iIiIiIgBoSygABc6zbAAA/v56QAAAAA==";
img.onload = function() {
img.src = "data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAQAAAAfQ//73v/+BiOh/AAA=";
};

// Error handler will be executed if the browser doesn't support webp.
img.onerror = function() {
eugene-manuilov marked this conversation as resolved.
Show resolved Hide resolved
// Loop through already available images.
loadMediaDetails( document.querySelectorAll( 'img' ) );

// Start the mutation observer to update images added dynamically.
var observer = new MutationObserver( function( mutationList ) {
for ( var i = 0; i < mutationList.length; i++ ) {
loadMediaDetails( mutationList[i].addedNodes );
}
} );

observer.observe( document.body, {
subtree: true,
childList: true,
} );
};
} )();
</script>
<?php
}
add_action( 'wp_footer', 'webp_uploads_wepb_fallback' );
eugene-manuilov marked this conversation as resolved.
Show resolved Hide resolved