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

Lightbox: Re-use existing Tag Processor instance. #5428

Draft
wants to merge 1 commit into
base: trunk
Choose a base branch
from
Draft
Changes from all 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
151 changes: 94 additions & 57 deletions src/wp-includes/blocks/image.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,21 +124,31 @@ function block_core_image_get_lightbox_settings( $block ) {
*/
function block_core_image_render_lightbox( $block_content, $block ) {
$processor = new WP_HTML_Tag_Processor( $block_content );
if ( ! $processor->next_tag() ) {
return $block_content;
}

/*
* This code jumps around a fair amount, so bookmarking the first
* tag provides the ability to "reset" to and start scanning
* again without recreating a new Tag Processor instance.
*/
$processor->set_bookmark( 'first tag' );

$aria_label = __( 'Enlarge image' );
// The first tag that was already found could be an image, but it may not be.
if ( 'IMG' !== $processor->get_tag() && ! $processor->next_tag( 'IMG' ) ) {
return $block_content;
}

$processor->next_tag( 'img' );
$alt_attribute = $processor->get_attribute( 'alt' );
$alt_attribute = is_string( $alt_attribute ) ? trim( $alt_attribute ) : null;

// An empty alt attribute `alt=""` is valid for decorative images.
if ( is_string( $alt_attribute ) ) {
$alt_attribute = trim( $alt_attribute );
}

// It only makes sense to append the alt text to the button aria-label when the alt text is non-empty.
if ( $alt_attribute ) {
if ( ! empty( $alt_attribute ) ) {
/* translators: %s: Image alt text. */
$aria_label = sprintf( __( 'Enlarge image: %s' ), $alt_attribute );
} else {
$aria_label = __( 'Enlarge image' );
}

// Currently, we are only enabling the zoom animation.
Expand All @@ -163,12 +173,16 @@ function block_core_image_render_lightbox( $block_content, $block ) {
$scale_attr = false;
}

$w = new WP_HTML_Tag_Processor( $block_content );
$w->next_tag( 'figure' );
$w->add_class( 'wp-lightbox-container' );
$w->set_attribute( 'data-wp-interactive', true );
// Jump back to the first tag and find the first figure.
$processor->seek( 'first tag' );
if ( 'FIGURE' !== $processor->get_tag() ) {
$processor->next_tag( 'FIGURE' );
}

$w->set_attribute(
$processor->set_bookmark( 'first figure' );
$processor->add_class( 'wp-lightbox-container' );
$processor->set_attribute( 'data-wp-interactive', true );
$processor->set_attribute(
'data-wp-context',
sprintf(
'{ "core":
Expand Down Expand Up @@ -196,14 +210,16 @@ function block_core_image_render_lightbox( $block_content, $block ) {
__( 'Enlarged image' )
)
);
$w->next_tag( 'img' );
$w->set_attribute( 'data-wp-init', 'effects.core.image.setCurrentSrc' );
$w->set_attribute( 'data-wp-on--load', 'actions.core.image.handleLoad' );
$w->set_attribute( 'data-wp-effect', 'effects.core.image.setButtonStyles' );
$w->set_attribute( 'data-wp-effect--setStylesOnResize', 'effects.core.image.setStylesOnResize' );
$body_content = $w->get_updated_html();

// Wrap the image in the body content with a button.

// Assume that an image appears within the currently-matched FIGURE element. It may not.
$processor->next_tag( 'img' );
$processor->set_attribute( 'data-wp-init', 'effects.core.image.setCurrentSrc' );
$processor->set_attribute( 'data-wp-on--load', 'actions.core.image.handleLoad' );
$processor->set_attribute( 'data-wp-effect', 'effects.core.image.setButtonStyles' );
$processor->set_attribute( 'data-wp-effect--setStylesOnResize', 'effects.core.image.setStylesOnResize' );
$body_content = $processor->get_updated_html();

// Insert a button in the body content before the image.
$img = null;
preg_match( '/<img[^>]+>/', $body_content, $img );

Expand All @@ -222,42 +238,63 @@ function block_core_image_render_lightbox( $block_content, $block ) {

$body_content = preg_replace( '/<img[^>]+>/', $button, $body_content );

// We need both a responsive image and an enlarged image to animate
// the zoom seamlessly on slow internet connections; the responsive
// image is a copy of the one in the body, which animates immediately
// as the lightbox is opened, while the enlarged one is a full-sized
// version that will likely still be loading as the animation begins.
$m = new WP_HTML_Tag_Processor( $block_content );
$m->next_tag( 'figure' );
$m->add_class( 'responsive-image' );
$m->next_tag( 'img' );
// We want to set the 'src' attribute to an empty string in the responsive image
// because otherwise, as of this writing, the wp_filter_content_tags() function in
// WordPress will automatically add a 'srcset' attribute to the image, which will at
// times cause the incorrectly sized image to be loaded in the lightbox on Firefox.
// Because of this, we bind the 'src' attribute explicitly the current src to reliably
// use the exact same image as in the content when the lightbox is first opened while
// we wait for the larger image to load.
$m->set_attribute( 'src', '' );
$m->set_attribute( 'data-wp-bind--src', 'context.core.image.imageCurrentSrc' );
$m->set_attribute( 'data-wp-style--object-fit', 'selectors.core.image.lightboxObjectFit' );
$initial_image_content = $m->get_updated_html();

$q = new WP_HTML_Tag_Processor( $block_content );
$q->next_tag( 'figure' );
$q->add_class( 'enlarged-image' );
$q->next_tag( 'img' );

// We set the 'src' attribute to an empty string to prevent the browser from loading the image
// on initial page load, then bind the attribute to a selector that returns the full-sized image src when
// the lightbox is opened. We could use 'loading=lazy' in combination with the 'hidden' attribute to
// accomplish the same behavior, but that approach breaks progressive loading of the image in Safari
// and Chrome (see https://github.com/WordPress/gutenberg/pull/52765#issuecomment-1674008151). Until that
// is resolved, manually setting the 'src' seems to be the best solution to load the large image on demand.
$q->set_attribute( 'src', '' );
$q->set_attribute( 'data-wp-bind--src', 'selectors.core.image.enlargedImgSrc' );
$q->set_attribute( 'data-wp-style--object-fit', 'selectors.core.image.lightboxObjectFit' );
$enlarged_image_content = $q->get_updated_html();
/*
* This code needs to generate a responsive image and an enlarged image
* to animate zoom seamlessly on slow internet connections; the responsive
* image is a copy of the one in the body, which animates immediately
* as the lightbox is opened, while the enlarged one is a full-sized
* version that will likely still be loading as the animation begins.
*
* In order to reuse the existing Tag Processor, changes made before
* need to be undone before setting the new changes here.
*/
$processor->seek( 'first figure' );
$processor->remove_class( 'wp-lightbox-container' );
$processor->remove_attribute( 'data-wp-context' );
$processor->remove_attribute( 'data-wp-interactive' );
$processor->add_class( 'responsive-image' );

$processor->seek( 'first figure' );
$processor->next_tag( 'img' );
$processor->remove_attribute( 'data-wp-init' );
$processor->remove_attribute( 'data-wp-on--load' );
$processor->remove_attribute( 'data-wp-effect' );
$processor->remove_attribute( 'data-wp-effect--setStylesOnResize' );

/*
* The 'src' attribute needs to be an empty string in the responsive image because
* otherwise, as of this writing, the wp_filter_content_tags() function in WordPress
* will automatically add a 'srcset' attribute to the image, which will at times
* cause the incorrectly sized image to be loaded in the lightbox on Firefox.
*
* Because of this, the 'src' attribute needs to be explicitly bound to the current
* src to reliably use the exact same image as in the content when the lightbox is
* first opened while waiting for the larger image to load.
*/
$processor->set_attribute( 'src', '' );
$processor->set_attribute( 'data-wp-bind--src', 'context.core.image.imageCurrentSrc' );
$processor->set_attribute( 'data-wp-style--object-fit', 'selectors.core.image.lightboxObjectFit' );
$initial_image_content = $processor->get_updated_html();

/*
* Reusing the existing Tag Processor again requires resetting state.
*/
$processor->seek( 'first figure' );
$processor->remove_class( 'responsive-image' );
$processor->add_class( 'enlarged-image' );

/*
* It's necessary to set the 'src' attribute to an empty string to prevent the browser from loading the image
* on initial page load, then to bind the attribute to a selector that returns the full-sized image src when
* the lightbox is opened. The combination of 'loading=lazy' with the 'hidden' attribute could be used to
* accomplish the same behavior, but that approach breaks progressive loading of the image in Safari
* and Chrome (see https://github.com/WordPress/gutenberg/pull/52765#issuecomment-1674008151). Until that
* is resolved, manually setting the 'src' loads the large image on demand without causing renderering issues.
*/
$processor->next_tag( 'img' );
$processor->set_attribute( 'data-wp-bind--src', 'selectors.core.image.enlargedImgSrc' );
$processor->set_attribute( 'data-wp-style--object-fit', 'selectors.core.image.lightboxObjectFit' );
$enlarged_image_content = $processor->get_updated_html();

// If the current theme does NOT have a `theme.json`, or the colors are not defined,
// we need to set the background color & close button color to some default values
Expand Down
Loading