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

Account for responsive images being disabled when generating a PICTURE element #1449

Merged
merged 15 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions plugins/webp-uploads/helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -406,3 +406,61 @@ function webp_uploads_is_picture_element_enabled(): bool {
function webp_uploads_is_jpeg_fallback_enabled(): bool {
return (bool) get_option( 'perflab_generate_webp_and_jpeg' );
}

/**
* Retrieves the image URL for a specified MIME type from the attachment metadata.
*
* This function attempts to locate an alternate image source URL in the
* attachment's metadata that matches the provided MIME type.
*
* @since n.e.x.t
*
* @param int $attachment_id The ID of the attachment.
* @param string $src The original image src url.
* @param string $mime A mime type we are looking to get image url.
* @return string|false Returns mime type image if available, false otherwise.
mukeshpanchal27 marked this conversation as resolved.
Show resolved Hide resolved
*/
function webp_uploads_get_mime_type_image( int $attachment_id, string $src, string $mime ) {
mukeshpanchal27 marked this conversation as resolved.
Show resolved Hide resolved
$metadata = wp_get_attachment_metadata( $attachment_id );
$src_basename = wp_basename( $src );
if ( isset( $metadata['sources'][ $mime ]['file'] ) ) {
$basename = wp_basename( $metadata['file'] );

if ( $src_basename === $basename ) {
mukeshpanchal27 marked this conversation as resolved.
Show resolved Hide resolved
return str_replace(
$basename,
$metadata['sources'][ $mime ]['file'],
$src
);
}
}

if ( isset( $metadata['sizes'] ) && is_array( $metadata['sizes'] ) ) {
foreach ( $metadata['sizes'] as $size => $size_data ) {

if ( empty( $size_data['file'] ) ) {
Copy link
Member

Choose a reason for hiding this comment

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

Can we avoid using empty() here?

continue;
}

if ( ! isset( $size_data['sources'][ $mime ]['file'] ) ) {
continue;
}

if ( $size_data['file'] === $size_data['sources'][ $mime ]['file'] ) {
continue;
}

if ( $src_basename !== $size_data['file'] ) {
continue;
}

return str_replace(
$size_data['file'],
$size_data['sources'][ $mime ]['file'],
$src
);
}
}

return false;
mukeshpanchal27 marked this conversation as resolved.
Show resolved Hide resolved
}
69 changes: 41 additions & 28 deletions plugins/webp-uploads/picture-element.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,38 +103,51 @@ function webp_uploads_wrap_image_in_picture( string $image, string $context, int
list( $src, $width, $height ) = $image_src;
$size_array = array( absint( $width ), absint( $height ) );

// Get the sizes from the IMG tag.
$sizes = $processor->get_attribute( 'sizes' );
// Gets the srcset and sizes from the IMG tag.
$sizes = $processor->get_attribute( 'sizes' );
$srcset = $processor->get_attribute( 'srcset' );

foreach ( $mime_types as $image_mime_type ) {
// Filter core's wp_get_attachment_image_srcset to return the sources for the current mime type.
$filter = static function ( $sources ) use ( $mime_type_data, $image_mime_type ): array {
$filtered_sources = array();
foreach ( $sources as $source ) {
// Swap the URL for the current mime type.
if ( isset( $mime_type_data[ $image_mime_type ][ $source['descriptor'] ][ $source['value'] ] ) ) {
$filename = $mime_type_data[ $image_mime_type ][ $source['descriptor'] ][ $source['value'] ]['file'];
$filtered_sources[] = array(
'url' => dirname( $source['url'] ) . '/' . $filename,
'descriptor' => $source['descriptor'],
'value' => $source['value'],
);
if ( null !== $srcset && null !== $sizes ) {
foreach ( $mime_types as $image_mime_type ) {
// Filter core's wp_get_attachment_image_srcset to return the sources for the current mime type.
$filter = static function ( $sources ) use ( $mime_type_data, $image_mime_type ): array {
$filtered_sources = array();
foreach ( $sources as $source ) {
// Swap the URL for the current mime type.
if ( isset( $mime_type_data[ $image_mime_type ][ $source['descriptor'] ][ $source['value'] ] ) ) {
$filename = $mime_type_data[ $image_mime_type ][ $source['descriptor'] ][ $source['value'] ]['file'];
$filtered_sources[] = array(
'url' => dirname( $source['url'] ) . '/' . $filename,
'descriptor' => $source['descriptor'],
'value' => $source['value'],
);
}
}
return $filtered_sources;
};
add_filter( 'wp_calculate_image_srcset', $filter );
$image_srcset = wp_get_attachment_image_srcset( $attachment_id, $size_array, $image_meta );
remove_filter( 'wp_calculate_image_srcset', $filter );
if ( is_string( $image_srcset ) ) {
$picture_sources .= sprintf(
'<source type="%s" srcset="%s"%s>',
esc_attr( $image_mime_type ),
esc_attr( $image_srcset ),
is_string( $sizes ) ? sprintf( ' sizes="%s"', esc_attr( $sizes ) ) : ''
);
}
}
} else {
foreach ( $mime_types as $image_mime_type ) {
$image_srcset = webp_uploads_get_mime_type_image( $attachment_id, $src, $image_mime_type );
if ( is_string( $image_srcset ) ) {
$picture_sources .= sprintf(
'<source type="%s" srcset="%s">',
esc_attr( $image_mime_type ),
esc_attr( $image_srcset )
);
}
return $filtered_sources;
};
add_filter( 'wp_calculate_image_srcset', $filter );
$image_srcset = wp_get_attachment_image_srcset( $attachment_id, $size_array, $image_meta );
remove_filter( 'wp_calculate_image_srcset', $filter );
if ( false === $image_srcset ) {
continue;
}
$picture_sources .= sprintf(
'<source type="%s"%s%s>',
esc_attr( $image_mime_type ),
is_string( $image_srcset ) ? sprintf( ' srcset="%s"', esc_attr( $image_srcset ) ) : '',
is_string( $sizes ) ? sprintf( ' sizes="%s"', esc_attr( $sizes ) ) : ''
);
}

return sprintf(
Expand Down
38 changes: 33 additions & 5 deletions plugins/webp-uploads/tests/test-picture-element.php
Original file line number Diff line number Diff line change
Expand Up @@ -260,9 +260,14 @@ function ( string $content ): string {
);
}

public function test_disable_responsive_image_with_picture_element(): void {
/**
* @dataProvider data_provider_test_disable_responsive_image_with_picture_element
*
* @param Closure $set_up Set up the filter.
*/
public function test_disable_responsive_image_with_picture_element( Closure $set_up ): void {
// Disable responsive images.
add_filter( 'wp_calculate_image_sizes', '__return_false' );
$set_up();

// Create some content with the image.
$image = wp_get_attachment_image(
Expand All @@ -281,16 +286,19 @@ public function test_disable_responsive_image_with_picture_element(): void {
$this->assertTrue( $img_processor->next_tag( array( 'tag_name' => 'IMG' ) ), 'There should be an IMG tag.' );
$img_src = $img_processor->get_attribute( 'src' );
$this->assertStringEndsWith( '.webp', $img_src, 'Make sure the IMG should return WEBP src.' );
$this->assertNull( $img_processor->get_attribute( 'sizes' ), 'Make sure the sizes attribute is missing in IMG tag.' );
$this->assertNull( $img_processor->get_attribute( 'srcset' ), 'Make sure the srcset attribute is missing in IMG tag.' );
$this->assertNull( $img_processor->get_attribute( 'sizes' ), 'Make sure that there is no sizes attribute in IMG tag.' );
$this->assertNull( $img_processor->get_attribute( 'srcset' ), 'Make sure that there is no srcset attribute in IMG tag.' );
mukeshpanchal27 marked this conversation as resolved.
Show resolved Hide resolved

// Apply picture element support.
$this->opt_in_to_picture_element();

$picture_markup = apply_filters( 'the_content', $image );

$picture_processor = new WP_HTML_Tag_Processor( $picture_markup );
$picture_processor->next_tag( array( 'tag_name' => 'IMG' ) );
$this->assertTrue( $picture_processor->next_tag( array( 'tag_name' => 'PICTURE' ) ), 'Make sure that there is a PICTURE tag.' );
$this->assertTrue( $picture_processor->next_tag( array( 'tag_name' => 'IMG' ) ), 'Make sure that there is a IMG tag.' );
$this->assertNull( $picture_processor->get_attribute( 'sizes' ), 'Make sure that there is no sizes attribute in IMG tag.' );
$this->assertNull( $picture_processor->get_attribute( 'srcset' ), 'Make sure that there is no srcset attribute in IMG tag.' );

// The fallback image should be JPEG.
$this->assertStringEndsWith( '.jpg', $picture_processor->get_attribute( 'src' ), 'Make sure the fallback IMG should return JPEG src.' );
Expand All @@ -302,6 +310,26 @@ public function test_disable_responsive_image_with_picture_element(): void {
}
}

/**
* Data provider for it_should_maybe_wrap_images_in_picture_element.
*
* @return array<string, array{ add_filter: Closure }>
*/
public function data_provider_test_disable_responsive_image_with_picture_element(): array {
return array(
'no_sizes' => array(
'add_filter' => static function (): void {
add_filter( 'wp_calculate_image_sizes', '__return_false' );
},
),
'no_srcset' => array(
'add_filter' => static function (): void {
add_filter( 'wp_calculate_image_srcset', '__return_false' );
},
),
);
}

public function test_picture_source_should_have_full_size_image_in_its_srcset(): void {
// Create some content with the image.
$image = wp_get_attachment_image(
Expand Down